package com.findsing.jetty.websocket.server.config;

import java.util.Iterator;

import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpSession;
import javax.websocket.DeploymentException;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;

import org.apache.log4j.Logger;
import org.eclipse.jetty.websocket.jsr356.server.AnnotatedServerEndpointConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.context.WebApplicationContext;

import com.findsing.jetty.websocket.server.endpoint.BroadcastServerEndpoint;

@Configuration
@Import({ ApplicationConfig.class })
public class WebSocketConfig {
	private static final Logger logger = Logger.getLogger(WebSocketConfig.class.getName());
	
	final boolean threadSafe = false;
	
	@Autowired
	private WebApplicationContext context;
	private ServerContainer container;

	public class SpringServerEndpointConfigurator extends ServerEndpointConfig.Configurator {

		@Override
		public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
			T endpoint = context.getAutowireCapableBeanFactory().createBean(endpointClass);
			/**
			 * Set session handler for endpoint, use for retrieve httpSession.
			 * 
			 * TODO: It's better to inject this variable from Spring context config. But current
			 * version is not thread safe.
			 * 
			 * (BaseEndpoint)endpoint).setSessionHandler(ServerStarter.getSessionHandler());
			 */

			return endpoint;
		}
		
		@Override
		public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
            super.modifyHandshake(sec, request, response);

            logger.debug("Handshaking...");
            
            /**
             * Because ServerEndpointConfig is not thread safe, so this may not work.
             * 
             * This function is temporary disabled, this means endpoint instance cannot get httpSession from user properties
             * Instead by receiving session id enclosed in the first message sent from client after connected.
             */
            if(threadSafe)
            {
            	synchronized(sec)
            	{
	               	// Save HttpSession
	               	HttpSession httpSession = (HttpSession)request.getHttpSession();
	               	sec.getUserProperties().put(HttpSession.class.getName(), httpSession);
            	}
            }

           	// Iterate headers.
            Map<String, List<String>> headers = request.getHeaders();
            Iterator<String> names = headers.keySet().iterator();
            while(names.hasNext())
            {
            	String name = names.next();
            	List<String> values = headers.get(name);
            	for(String value: values)
            	{
            		logger.info(String.format("%s: %s", name, value));

            		/*if(name == "Authorization")
	                {
	                	try
	                	{
		               		String authenticationToken = new String(Base64.decodeBase64(value.split(" ")[1].getBytes(Charset.forName("UTF-8"))), "UTF-8");
		               		String[] princple = authenticationToken.split(":");
		               		logger.info(String.format("\"%s\" is trying to authorize by: \"%s\"", princple[0], princple[1]));
	                	}
	                	catch(UnsupportedEncodingException e)
	                	{
	                		logger.error(e.getMessage(), e);
	                	}
	                }*/
	           	}
            }
        }
	}

	@Bean
	public ServerEndpointConfig.Configurator configurator() {
		return new SpringServerEndpointConfigurator();
	}

	@PostConstruct
	public void init() throws DeploymentException {
		container = (ServerContainer) context.getServletContext().getAttribute(javax.websocket.server.ServerContainer.class.getName());

		container.addEndpoint(new AnnotatedServerEndpointConfig(
				BroadcastServerEndpoint.class,
				BroadcastServerEndpoint.class.getAnnotation(ServerEndpoint.class)) {

			@Override
			public Configurator getConfigurator() {
				return configurator();
			}
		});
	}
}
